/**************************************************************************
	File		: osal_class.c
	Author		: TaoLei Zhang
	Date		: 2018-9-27
***************************************************************************/

/**************************************************************************
    								include
***************************************************************************/
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/idr.h>
#include <linux/slab.h>

#include "osal_class.h"
#include "osal_log.h"
#include "osal_fs.h"
/**************************************************************************
    								macro
***************************************************************************/
#define toOsalClassDevice(d) container_of(d, OsalClassDevice_t, dev)

/**************************************************************************
    								typedef
***************************************************************************/
typedef struct tagOsalClassCtx_t
{
	struct class *pClass;
	dev_t devt;
	char name[32];
	OsalProcHandle procDirHdl;
	struct ida ida;
}OsalClassCtx_t;

typedef struct tagOsalClassDevice_t
{
	struct device dev;
	struct module *owner;
	int id;
	char name[32];
	struct file_operations* pOps;
	OsalProcHandle classProcHdl;
}OsalClassDevice_t;

	
/**************************************************************************
    								variables
***************************************************************************/


	
/**************************************************************************
    								functions
***************************************************************************/
OsalClassHandle OsalClassCreate(char *pName, void *pOwner)
{
	int ret;
	OsalClassCtx_t *pCtx = NULL;

	pCtx = kmalloc(sizeof(OsalClassCtx_t), GFP_KERNEL);
	if (NULL == pCtx)
	{
		return NULL;
	}

	strcpy(pCtx->name, pName);
	pCtx->pClass = class_create((struct module*)pOwner, pCtx->name);
	if (IS_ERR(pCtx->pClass))
	{
		OsalLog(Log_Error, "class_create error %d.\n", PTR_ERR(pCtx->pClass));
		goto err;
	}	

	ret = alloc_chrdev_region(&pCtx->devt, 0, 256, pCtx->name);
	if (0 > ret)
	{
		OsalLog(Log_Error, "alloc_chrdev_region error %d.\n", ret);
		goto err;
	}

	pCtx->procDirHdl = OsalProcMkdir(pCtx->name, NULL);
	if (NULL == pCtx->procDirHdl)
	{
		OsalLog(Log_Error, "OsalProcMkdir error.\n");
		goto err;
	}
	
	ida_init(&pCtx->ida);
	return (OsalClassHandle)pCtx;
	
err:
	kfree(pCtx);
	return NULL;
}
EXPORT_SYMBOL(OsalClassCreate);

static void osalClassDeviceRelease(struct device *dev)
{
	OsalClassDevice_t *pClassDevice = toOsalClassDevice(dev);
	OsalClassCtx_t *pCtx = pClassDevice->classProcHdl;
	
	ida_simple_remove(&pCtx->ida, pClassDevice->id);
	kfree(pClassDevice);
}

OsalClassDeviceHandle OsalClassDeviceRegister(OsalClassHandle				hdl,
														const char						*pName, 
														struct device					*pDev, 
														struct file_operations			*pOps,
														struct module					*pOwner)
{
	OsalClassCtx_t *pCtx = (OsalClassCtx_t*)hdl;
	OsalClassDevice_t *pClassDevice;
	int id, err;

	id = ida_simple_get(&pCtx->ida, 0, 0, GFP_KERNEL);
	if (id < 0) {
		err = id;
		goto exit;
	}

	pClassDevice = kzalloc(sizeof(OsalClassDevice_t), GFP_KERNEL);
	if (pClassDevice == NULL) {
		err = -ENOMEM;
		goto exit_ida;
	}

	pClassDevice->id = id;
	pClassDevice->pOps = pOps;
	pClassDevice->owner = pOwner;
	pClassDevice->dev.parent = pDev;
	pClassDevice->dev.class = pCtx->pClass;
	pClassDevice->dev.release = osalClassDeviceRelease;

	//TODO:
exit_kfree:
	kfree(pClassDevice);

exit_ida:
	ida_simple_remove(&pCtx->ida, id);
	
exit:
	OsalLog(Log_Error, "unable register %s %d\n", pName, err);
	return ERR_PTR(err);

	
}


void OsalClassDestroy(OsalClassHandle hdl)
{
	OsalClassCtx_t *pCtx = (OsalClassCtx_t*)hdl;

	if (pCtx->procDirHdl)
	{
		OsalProcRemoveDir(pCtx->name, NULL);
	}

	unregister_chrdev_region(pCtx->devt, 256);
	if (pCtx->pClass != NULL && !IS_ERR(pCtx->pClass))
	{
		class_unregister(pCtx->pClass);
	}
	ida_destroy(&pCtx->ida);
	kfree(pCtx);
}
EXPORT_SYMBOL(OsalClassDestroy);



